基于u

您所在的位置:网站首页 range 车牌 基于u

基于u

2024-07-10 18:48| 来源: 网络整理| 查看: 265

本文链接:https://blog.csdn.net/qq_32194791/article/details/106748685,转载请注明出处

完整项目已上传至github: https://github.com/duanshengliu/End-to-end-for-chinese-plate-recognition/tree/master/License-plate-recognition 喜欢的话顺手点个star,谢谢支持 整体思路:1.利用u-net图像分割得到二值化图像,2.再使用cv2进行边缘检测获得车牌区域坐标,并将车牌图形矫正,3.利用卷积神经网络cnn进行车牌多标签端到端识别 实现效果:拍摄角度倾斜、强曝光或昏暗环境等都能较好地识别,甚至有些百度AI车牌识别未能识别的图片也能识别 环境:python:3.6, tensorflow:1.15.2, opencv: 4.1.0.25 1.车牌定位

首先贴一下图像分割的效果图:

我们可以通过图像分割算法对一张输入图片进行分割,分割后的图形其实是对原图中的区域进行的分类标注,例如这里我们可以将原图标注为2类,一类就是车牌区域,还有一类就是无关的背景区域。说到标注图形就不得不说labelme了,我们可以在cmd界面通过命令 pip install labelme 进行labelme库的安装,安装结束在cmd界面输入labelme即可打开lablem软件的标注界面如下:

1. 点击OpenDir ,选择我们准备好的车辆数据集(注意:一定要先把图片全都resize为训练时所需的大小,再进行标注。我们知道图片数据的范围是0-255,背景为黑色0,车牌区域为255,我们需要的是标注好的图片即img_mask中值只有{0,255}这2种,如果我们不先resize,标注完再resize会导致一个大问题,就是数据的值并不是二类,会出现{0,1,10,248,251,255}等类似的多值问题,我在之前就遇到这样的问题,不得已又重新标注了300多张图)

2. 点击左上角File—>将Save Automatically勾选上,点击Change Output Dir选择保存路径,我这里是在桌面D:/desktop/下新建了一个文件夹命名为labelme,在labelm文件夹中新建了一个json文件夹用于保存我们标注的json数据,这里我们Change Output Dir的保存路径就选它,还新建了一个data文件夹用于存放后续转换的图片数据,而待标注图片在pic文件中,存放的都是resize好的512×512的图片,命名格式最好像我这样

3. 准备好上述一切就可以开始标注了,点击软件左侧的   这是画任意多边形的按钮,鼠标左键点击进行标注,最后双击鼠标左键会锁定标注区域,出现如下图界面,第一次标注需输入名称,后续标注就自动显示了,点击ok后标注的线条变为红色,同时json文件夹也会相应保存和pic名字对应的json文件:

4. 全部标注结束后,使用如下代码将json数据提取出来并保存到train_image和train_label文件夹中,u-net部分的数据集我一共标注了1200多张,最终效果很棒,达到了定位的效果

import os import cv2 import numpy as np #将json文件label转换为到data文件夹 n=1200#n为总共标注的图片数 for i in range(n): os.system('labelme_json_to_dataset D:/desktop/labelme/json/%d.json -o D:/desktop/labelme/data/%d_json'%(i,i)) #dst_w=512 #dst_h=512 #dst_shape=(dst_w,dst_h,3) train_image = 'D:/desktop/labelme/train_image/' if not os.path.exists(train_image): os.makedirs(train_image) train_label = 'D:/desktop/labelme/train_label/' if not os.path.exists(train_label): os.makedirs(train_label) for i in range(n): print(i) img=cv2.imread('D:/desktop/labelme/data/%d_json/img.png'%i) label=cv2.imread('D:/desktop/labelme/data/%d_json/label.png'%i) print(img.shape) label=label/np.max(label[:,:,2])*255 label[:,:,0]=label[:,:,1]=label[:,:,2] print(np.max(label[:,:,2])) # cv2.imshow('l',label) # cv2.waitKey(0) print(set(label.ravel())) cv2.imwrite(train_image+'%d.png'%i,img) cv2.imwrite(train_label+'%d.png'%i,label)

 

这样一来,标注好的u-net训练图片就准备好了,分别在train_image和train_label文件夹中,一并放在unet_datasets文件夹内,如下图所示:

接下来是u-net模型搭建和训练,使用tensorflow的keras实现,贴一下我训练u-net用的代码:

def unet_train(): height = 512 width = 512 path = 'D:/desktop/unet_datasets/' input_name = os.listdir(path + 'train_image') n = len(input_name) print(n) X_train, y_train = [], [] for i in range(n): print("正在读取第%d张图片" % i) img = cv2.imread(path + 'train_image/%d.png' % i) label = cv2.imread(path + 'train_label/%d.png' % i) X_train.append(img) y_train.append(label) X_train = np.array(X_train) y_train = np.array(y_train) def Conv2d_BN(x, nb_filter, kernel_size, strides=(1, 1), padding='same'): x = layers.Conv2D(nb_filter, kernel_size, strides=strides, padding=padding)(x) x = layers.BatchNormalization(axis=3)(x) x = layers.LeakyReLU(alpha=0.1)(x) return x def Conv2dT_BN(x, filters, kernel_size, strides=(2, 2), padding='same'): x = layers.Conv2DTranspose(filters, kernel_size, strides=strides, padding=padding)(x) x = layers.BatchNormalization(axis=3)(x) x = layers.LeakyReLU(alpha=0.1)(x) return x inpt = layers.Input(shape=(height, width, 3)) conv1 = Conv2d_BN(inpt, 8, (3, 3)) conv1 = Conv2d_BN(conv1, 8, (3, 3)) pool1 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv1) conv2 = Conv2d_BN(pool1, 16, (3, 3)) conv2 = Conv2d_BN(conv2, 16, (3, 3)) pool2 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv2) conv3 = Conv2d_BN(pool2, 32, (3, 3)) conv3 = Conv2d_BN(conv3, 32, (3, 3)) pool3 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv3) conv4 = Conv2d_BN(pool3, 64, (3, 3)) conv4 = Conv2d_BN(conv4, 64, (3, 3)) pool4 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv4) conv5 = Conv2d_BN(pool4, 128, (3, 3)) conv5 = layers.Dropout(0.5)(conv5) conv5 = Conv2d_BN(conv5, 128, (3, 3)) conv5 = layers.Dropout(0.5)(conv5) convt1 = Conv2dT_BN(conv5, 64, (3, 3)) concat1 = layers.concatenate([conv4, convt1], axis=3) concat1 = layers.Dropout(0.5)(concat1) conv6 = Conv2d_BN(concat1, 64, (3, 3)) conv6 = Conv2d_BN(conv6, 64, (3, 3)) convt2 = Conv2dT_BN(conv6, 32, (3, 3)) concat2 = layers.concatenate([conv3, convt2], axis=3) concat2 = layers.Dropout(0.5)(concat2) conv7 = Conv2d_BN(concat2, 32, (3, 3)) conv7 = Conv2d_BN(conv7, 32, (3, 3)) convt3 = Conv2dT_BN(conv7, 16, (3, 3)) concat3 = layers.concatenate([conv2, convt3], axis=3) concat3 = layers.Dropout(0.5)(concat3) conv8 = Conv2d_BN(concat3, 16, (3, 3)) conv8 = Conv2d_BN(conv8, 16, (3, 3)) convt4 = Conv2dT_BN(conv8, 8, (3, 3)) concat4 = layers.concatenate([conv1, convt4], axis=3) concat4 = layers.Dropout(0.5)(concat4) conv9 = Conv2d_BN(concat4, 8, (3, 3)) conv9 = Conv2d_BN(conv9, 8, (3, 3)) conv9 = layers.Dropout(0.5)(conv9) outpt = layers.Conv2D(filters=3, kernel_size=(1, 1), strides=(1, 1), padding='same', activation='relu')(conv9) model = models.Model(inpt, outpt) model.compile(optimizer='adam', loss='mean_squared_error', metrics=['accuracy']) model.summary() print(np.max(X_train)) print(np.max(y_train)) print(X_train.shape) model.fit(X_train, y_train, epochs=100, batch_size=15)#epochs和batch_size看个人情况调整,batch_size不要过大,否则内存容易溢出 #我11G显存也只能设置15-20左右,我训练最终loss降低至250左右,acc约95%左右 model.save('unet.h5') print('unet.h5保存成功!!!') 2.车牌矫正

接着训练u-net得到unet.h5。我们可以先利用百度API进行车牌标注,那部分可以见我上一篇博客:https://blog.csdn.net/qq_32194791/article/details/106526217,那里讲述了如何高效的将整张图片标注好,标注好再利用u-net进行图像分割就可以轻松高效地获得标注好的车牌图片了,贴一下u-net分割和cv2矫正的代码

import os import cv2 import numpy as np from tensorflow import keras def unet_predict(unet,img_src_path): img_src= cv2.imdecode(np.fromfile(img_src_path, dtype=np.uint8), -1)#从中文路径读取时用 # img_src=cv2.imread(img_src_path) if img_src.shape!=(512,512,3): img_src=cv2.resize(img_src,dsize=(512,512))[:,:,:3]#dsize=(宽度,高度),[:,:,:3]是防止图片为4通道图片,后续无法reshape img_src=img_src.reshape(1,512,512,3)#预测图片shape为(1,512,512,3) img_mask=unet.predict(img_src)#归一化除以255后进行预测 img_src=img_src.reshape(512,512,3)#将原图reshape为3维 img_mask=img_mask.reshape(512,512,3)#将预测后图片reshape为3维 img_mask=img_mask/np.max(img_mask)*255#归一化后乘以255 img_mask[:,:,2]=img_mask[:,:,1]=img_mask[:,:,0]#三个通道保持相同 img_mask=img_mask.astype(np.uint8)#将img_mask类型转为int型 return img_src,img_mask def locate(img_src,img_mask,name): """ 该函数通过cv2对img_mask进行边缘检测,获取车牌区域的边缘坐标(存储在contours中)和最小外接矩形4个端点坐标, 再从车牌的边缘坐标中计算出和最小外接矩形4个端点最近的点即为平行四边形车牌的四个端点,从而实现车牌的定位和矫正 :param img_src: 原始图片 :param img_mask: 通过u_net预测得到的二值化图片,车牌区域呈现白色,背景区域为黑色 :return: 定位且矫正后的车牌 """ contours, hierarchy = cv2.findContours(img_mask[:, :, 0], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not len(contours):#contours1长度为0说明未检测到车牌 print("未检测到车牌") else: flag=0#默认flag为0,因为不一定有车牌区域 for ii,cont in enumerate(contours): x, y, w, h = cv2.boundingRect(cont)#获取最小外接矩形 img_cut_mask=img_mask[y:y+h,x:x+w]#将标签车牌区域截取出来 if np.mean(img_cut_mask)>=75 and w>15 and h>15: rect = cv2.minAreaRect(cont)#针对坐标点获取带方向角的最小外接矩形,中心点坐标,宽高,旋转角度 box = cv2.boxPoints(rect).astype(np.int32)#获取最小外接矩形四个顶点坐标 cv2.drawContours(img_mask, contours, -1, (0, 0, 255), 2) cv2.drawContours(img_mask, [box], 0, (0, 255, 0), 2) cont=cont.reshape(-1,2).tolist() #由于转换矩阵的两组坐标位置需要一一对应,因此需要将最小外接矩形的坐标进行排序,最终排序为[左上,左下,右上,右下] box=sorted(box,key=lambda xy:xy[0])#先按照左右进行排序,分为左侧的坐标和右侧的坐标 box_left,box_right=box[:2],box[2:]#此时box的前2个是左侧的坐标,后2个是右侧的坐标 box_left=sorted(box_left,key=lambda x:x[1])#再按照上下即y进行排序,此时box_left中为左上和左下两个端点坐标 box_right=sorted(box_right,key=lambda x:x[1])#此时box_right中为右上和右下两个端点坐标 box=np.array(box_left+box_right)#[左上,左下,右上,右下] x0,y0=box[0][0],box[0][1]#这里的4个坐标即为最小外接矩形的四个坐标,接下来需获取平行(或不规则)四边形的坐标 x1,y1=box[1][0],box[1][1] x2,y2=box[2][0],box[2][1] x3,y3=box[3][0],box[3][1] def point_to_line_distance(X,Y): if x2-x0: k_up=(y2-y0)/(x2-x0)#斜率不为无穷大 d_up=abs(k_up*X-Y+y2-k_up*x2)/(k_up**2+1)**0.5 else:#斜率无穷大 d_up=abs(X-x2) if x1-x3: k_down=(y1-y3)/(x1-x3)#斜率不为无穷大 d_down=abs(k_down*X-Y+y1-k_down*x1)/(k_down**2+1)**0.5 else:#斜率无穷大 d_down=abs(X-x1) return d_up,d_down d0,d1,d2,d3=np.inf,np.inf,np.inf,np.inf l0,l1,l2,l3=(x0,y0),(x1,y1),(x2,y2),(x3,y3) for each in cont: #计算cont中的坐标与矩形四个坐标的距离以及到上下两条直线的距离,对距离和进行权重的添加,成功选出四边形的4个顶点坐标 x,y=each[0],each[1] dis0=(x-x0)**2+(y-y0)**2 dis1=(x-x1)**2+(y-y1)**2 dis2=(x-x2)**2+(y-y2)**2 dis3=(x-x3)**2+(y-y3)**2 d_up,d_down=point_to_line_distance(x,y) weight=0.975 if weight*d_up+(1-weight)*dis0


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3